﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI.WebControls;
using System.Web.UI;
using System.Web.DynamicData;
using System.Collections.Specialized;
using System.ComponentModel;

namespace Catalyst.Web.DynamicData
{
    public abstract class DynamicFilterContainerBase : DataBoundControl, INamingContainer, IWhereParametersProvider
    {
        protected const string SEARCH_IDENTIFIER_PREFIX = "qp_";
        protected const string COMPOSITE_PARAMETER_NAME = "DynamicParameters";

        private IPredicateDynamicDataSource _predicateDynamicDataSource;

        protected IPredicateDynamicDataSource PredicateDynamicDataSource
        {
            get
            {
                if (_predicateDynamicDataSource == null)
                {
                    _predicateDynamicDataSource = FindDataSource(this, DataSourceID);
                }
                return _predicateDynamicDataSource;
            }
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            if (!Page.IsPostBack)
            {
                if (string.Equals(Page.Request.QueryString["Action"], "Search", StringComparison.InvariantCultureIgnoreCase))
                {
                    LoadQueryStringParameters();
                    PopulateWherePredicateParameters();
                }
            }
        }

        public override void DataBind()
        {
            if (!ChildControlsCreated)
            {
                CreateChildControls();
                ChildControlsCreated = true;
            }
            base.DataBind();
        }

        protected override void PerformSelect() { }

        public IEnumerable<Parameter> GetWhereParameters(IDynamicDataSource dataSource)
        {
            EnsureChildControls();
            return GetWhereParameters(dataSource, this);
        }

        private IEnumerable<Parameter> GetWhereParameters(IDynamicDataSource dataSource, Control control)
        {
            IWhereParametersProvider provider;
            provider = control as IWhereParametersProvider;
            if (provider != null && provider != this)
            {
                foreach (Parameter p in provider.GetWhereParameters(dataSource))
                    yield return p;
            }
            foreach (Control child in control.Controls)
            {
                foreach (Parameter p in GetWhereParameters(dataSource, child))
                    yield return p;
            }
        }

        protected override bool OnBubbleEvent(object source, EventArgs args)
        {
            bool cancel = false;
            CommandEventArgs commandArgs = args as CommandEventArgs;
            if (commandArgs != null)
            {
                switch (commandArgs.CommandName.ToUpper())
                {
                    case "SEARCH":
                        ClearWherePredicateParameters();
                        PopulateWherePredicateParameters();
                        cancel = true;
                        break;
                    case "BROWSE":
                        BrowseToPath();
                        cancel = true;
                        break;
                    case "CLEAR":
                        ClearWherePredicateParameters();
                        ClearControlValues();
                        cancel = true;
                        break;
                }
            }

            return cancel;
        }

        private void ClearWherePredicateParameters()
        {
            IPredicateDynamicDataSource dataSource = PredicateDynamicDataSource;
            if (dataSource != null)
            {
                Parameter p = dataSource.WherePredicateParameters[COMPOSITE_PARAMETER_NAME];
                if (p != null)
                {
                    dataSource.WherePredicateParameters.Remove(p);
                    dataSource.WherePredicateParameters.SetDirty();
                }
            }
            else
                throw new InvalidOperationException("The DataSource must implement IPredicateDynamicDataSource. Is the datasource of type Catalyst.Web.DynamicData.DynamicLinqDataSource?");
        }

        private void PopulateWherePredicateParameters()
        {
            IPredicateDynamicDataSource dataSource = PredicateDynamicDataSource;
            if (dataSource != null)
            {
                AndExpressionParameter dynamicParameters = new AndExpressionParameter() { Name = COMPOSITE_PARAMETER_NAME };
                foreach (Parameter p in GetWhereParameters(dataSource))
                {
                    dynamicParameters.Parameters.Add(p);
                }
                dataSource.WherePredicateParameters.Add(dynamicParameters);
                dataSource.WherePredicateParameters.SetDirty();
            }
            else
                throw new InvalidOperationException("The DataSource must implement IPredicateDynamicDataSource. Is the datasource of type Catalyst.Web.DynamicData.DynamicLinqDataSource?");
        }

        public string SaveQueryStringParameters()
        {
            return SaveQueryStringParameters(Page.Request.Url.AbsolutePath);
        }

        public string SaveQueryStringParameters(string targetPage)
        {
            EnsureChildControls();
            NameValueCollection parameters = new NameValueCollection();
            SaveQueryStringParameters(parameters, this);

            StringBuilder sb = new StringBuilder();
            sb.Append(targetPage);
            sb.Append("?Action=Search");

            foreach (var key in parameters.AllKeys)
            {
                sb.AppendFormat("&{0}{1}={2}", SEARCH_IDENTIFIER_PREFIX, key, parameters[key]);
            }

            return sb.ToString();
        }

        private void SaveQueryStringParameters(NameValueCollection parameters, Control control)
        {
            FilterTemplateUserControlBase filterControl = control as FilterTemplateUserControlBase;
            if (filterControl != null)
            {
                parameters.Add(filterControl.SaveQueryStringParameters());
            }
            foreach (Control child in control.Controls)
            {
                SaveQueryStringParameters(parameters, child);
            }
        }

        public void LoadQueryStringParameters()
        {
            EnsureChildControls();

            NameValueCollection parameters = new NameValueCollection();
            NameValueCollection queryString = Page.Request.QueryString;
            int length = SEARCH_IDENTIFIER_PREFIX.Length;
            foreach (var key in queryString.AllKeys)
            {
                if (key.StartsWith(SEARCH_IDENTIFIER_PREFIX))
                {
                    parameters.Add(key.Remove(0, length), queryString[key]);
                }
            }

            LoadQueryStringParameters(parameters, this);
        }

        private void LoadQueryStringParameters(NameValueCollection parameters, Control control)
        {
            FilterTemplateUserControlBase filterControl = control as FilterTemplateUserControlBase;
            if (filterControl != null)
            {
                filterControl.LoadQueryStringParameters(parameters);
            }
            foreach (Control child in control.Controls)
            {
                LoadQueryStringParameters(parameters, child);
            }
        }

        private void BrowseToPath()
        {
            Page.Response.Redirect(SaveQueryStringParameters());
        }

        private void ClearControlValues()
        {
            ClearControlValues(this);
        }

        private void ClearControlValues(Control control)
        {
            FilterTemplateUserControlBase filterControl = control as FilterTemplateUserControlBase;
            if (filterControl != null)
            {
                filterControl.Clear();
            }
            foreach (Control child in control.Controls)
            {
                ClearControlValues(child);
            }
        }

        private IPredicateDynamicDataSource FindDataSource(Control control, string id)
        {
            if (control is Page)
                return null;

            IPredicateDynamicDataSource ds = control.FindControl(id) as IPredicateDynamicDataSource;
            if (ds != null)
            {
                return ds;
            }
            else
                return FindDataSource(control.Parent, id);
        }
    }
}
